package com.colbert.jknife.handler;

import com.colbert.jknife.annotation.AnnotationType;
import com.colbert.jknife.model.Constant;
import com.colbert.jknife.model.FieldCodeSet;
import com.colbert.jknife.model.ICodeSetManager;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import java.lang.annotation.Annotation;
import java.util.*;

public abstract class BaseAnnotationHandler implements IAnnotationHandler {

    private Messager messager;

    private Elements elements;

    public boolean handleAnnotation(RoundEnvironment roundEnv, ICodeSetManager codeSetManager) throws Exception {
        Class<? extends Annotation> annotation = getAnnotationType();
        if (annotation == null) {
            return false;
        }
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
        for (Element element : elements) {
            if (!checkElement(element)) {
                return false;
            }
            makeFieldCodeSet(element , codeSetManager);
        }
        return true;
    }

    public Class<? extends Annotation> getAnnotationType() {
        AnnotationType annotation = this.getClass().getAnnotation(AnnotationType.class);
        if (annotation == null) {
            error("No AnnotationType annotation " +
                "found on " + this.getClass().getName() +
                ", returning null.");
            return null;
        }
        return annotation.value();
    }

    protected CodeBlock.Builder createFieldBlock(int id) throws Exception {
        return CodeBlock.of("$L = $T.findViewById($L, $L, $L);", Constant.VIEW_LITERAL, Class.forName(Constant.CLASS_VIEWFINDER), Constant.PARENT_VIEW_LITERAL, Constant.TARGET, id).toBuilder();
    }

    public void attachProcessingEnvironment(ProcessingEnvironment processingEnv) {
        this.messager = processingEnv.getMessager();
        this.elements = processingEnv.getElementUtils();
    }

    protected String getClassName(Element element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        return typeElement.getQualifiedName().toString();
    }

    protected TypeName getTypeName(String qualifiedName) {
        return TypeName.get(elements.getTypeElement(qualifiedName).asType());
    }

    @Override
    public void checkFieldBlock(FieldCodeSet fieldCodeSet, int id) throws Exception {
        if(fieldCodeSet.getFieldCodeBlock() == null) {
            fieldCodeSet.setFieldCodeBlock(createFieldBlock(id).build());
        }
    }

    @Override
    public void addIntent(CodeBlock.Builder builder) {
        builder.add(CodeBlock.of("\n"));
        builder.indent().indent();
    }

    @Override
    public void subIntent(CodeBlock.Builder builder) {
        builder.add(CodeBlock.of("\n"));
        builder.unindent().unindent();
    }

    public void error(String msg) {
        error(msg, null);
    }

    public void error(String msg, Element element) {
        messager.printMessage(Diagnostic.Kind.ERROR, msg, element);
    }
}
